go GC

Posted by Liao on 2023-03-22

前言

之前已经学习过相关GC的知识,对于Go的GC细节还需要梳理一下。

GC过程

而三色抽象能够清晰地表现追踪式回收过程中对象的变化过程:

1、垃圾回收开始前,所有数据都为白色

2、把直接追踪到的root结点标记为灰色 (灰色代表基于当前结点展开的追踪还没完成)

3、当基于某个节点的追踪完成后(基于该节点追踪不到数据),便把该节点标记为黑色,表示它为存活数据,而且无需基于它进行追踪;

4、基于黑色结点追踪到的数据标为灰色,表示还需要基于它们展开追踪

5、当没有灰色结点时,标记任务结束,有用数据均为黑色,垃圾数据为白色。接着回收白色对象的内存即可

缺点

  • 容易造成内存碎片化

  • 避免存活对象误判为垃圾对象的情况,例如刚才识别为垃圾的对象被用户修改了,后面还需要用。这种情况通过强弱三色不变式解决

STW

STW (Stop the World) 暂停用户程序,只专注于垃圾回收。GO中的三色标记法是通过STW来保护的,其带来的问题是用户不能接受长时间的暂停。

但如果没有STW的保护,三色标记法容易出现对象被误删的情况,即原本不是垃圾的对象却被回收了。

因此可以通过强/弱三色不变式来解决上面的问题。三色标记中如果满足强/弱不变式之一,即使在没有STW的情况,即可保证对象不被误删的情况。

强三色不变式:无论如何,不允许黑色对象到白色对象的引用。避免把存货数据误判为垃圾的情况

弱三色不变式:可以出现黑色对象到白色对象的引用,但可以保证通过灰色对象可以抵达白色对象,或者可达白色对象的链路上存在灰色,也可避免错误回收

屏障机制

屏障其实是在代码执行过程中,添加额外的判断机制,避免存活对象丢失的问题,即强弱三色不变式的实现。有读写屏障、混合写屏障

写屏障

写屏障会在写操作中插入指令,目的是把数据对象的修改通知到垃圾回收器,因此写屏障都会有个记录集。

  • 插入写屏障:
    • 触发机制:当对象被引用时,触发的机制。
    • 具体操作:A对象引用B对象时,B对象标记为灰色。 可以把白色指针着为灰色,或者把写入的黑色对象退化为灰色
    • 作用:实现强三色不变式(不存在黑色对象引用白色对象的情况,因为白色对象强制变为灰色)
    • 缺点:只适用不在栈上的对象。因为栈的空间比较小,只适用函数调用(进栈、出栈)、小对象。需要在结束时通过启动STW重新扫描栈,大约用时10~100ms
  • 删除写屏障:
    • 触发机制:当对象被删除时,触发的机制。
    • 具体操作:被删除的对象,如果自身为灰色或者白色,那么被标记成灰色
    • 作用:实现弱三色不变式,对白色对象路径的破坏行为
    • 缺点:回收精度低,一个对象即使被删除了最后一个只想它的指针,也依然可以活过一轮扫描,在下一轮GC中被清除

混合写屏障

GO在V1.8引入三色标记法+混合写屏障,具体流程:

  1. GC开始前,所有节点都为白色
  2. 优先扫描全部栈对象,将可达对象标记为黑色(优化插入写屏障的要重新扫描栈对象的不足)
  3. 被删除的对象标记为灰色
  4. 被添加的对象标记为灰色